package gameObjects
{
	import adobe.utils.CustomActions;
	import Box2D.Collision.Shapes.b2Shape;
	import Box2D.Common.Math.b2Math;
	import Box2D.Common.Math.b2Transform;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2FixtureDef;
	import Box2D.Dynamics.Joints.b2Joint;
	import Box2D.Dynamics.Joints.b2PrismaticJoint;
	import Box2D.Dynamics.Joints.b2RevoluteJoint;
	import flash.display.DisplayObjectContainer;
	import flash.display.Stage;
	import flash.events.KeyboardEvent;
	import physics.PhysicsManager;
	import utilities.KeyboardInputManager;
	import utilities.Keys;
	/**
	 * ...
	 * @author Leong Shi Yun
	 */
	public class Car extends GameObject
	{
		// private const/var
		private const nMAX_STEER_ANGLE:Number = Math.PI / 4;
		private const nSTEER_SPEED:Number = 1.5;
		private const nSIDEWAYS_FRICTION_FORCE:Number = 0.25;
		private const nHORSEPOWER:Number = 0.5;
		private const nTORQUE:Number = 0.1;
		
		private const nWHEEL_X:Number = 25;
		private const nWHEEL_Y:Number = 30;
		
		private var m_stage:Stage = null;
		private var m_phyMgr:PhysicsManager = null;
		private var m_position:b2Vec2 = null;
		private var m_nEngineSpeed:Number = 0;
		private var m_nSteeringAngle:Number = 0;
		
		private var m_LFWheelBody:b2Body = null;
		private var m_RFWheelBody:b2Body = null;
		private var m_LRWheelBody:b2Body = null;
		private var m_RRWheelBody:b2Body = null;
		private var m_vBodies:Vector.<b2Body> = null;
		
		private var m_LFRevJoint:b2RevoluteJoint = null;
		private var m_RFRevJoint:b2RevoluteJoint = null;
		private var m_LRPrisJoint:b2PrismaticJoint = null;
		private var m_RRPrisJoint:b2PrismaticJoint = null;
		private var m_vJoints:Vector.<b2Joint> = null;
		
		/* public functions */
		public function Car(stage:Stage, physicsMgr:PhysicsManager, nX:Number, nY:Number, iTag:int)
		{
			tag = iTag;
			m_vBodies = new Vector.<b2Body>();
			m_vJoints = new Vector.<b2Joint>();
			
			if (stage) m_stage = stage;
			
			if (physicsMgr) m_phyMgr = physicsMgr;
			m_position = new b2Vec2(nX, nY);
			
			createCar();
			
			m_stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			m_stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
		}
		
		public function update():void
		{
			if (m_LFWheelBody && m_RFWheelBody && m_LRWheelBody && m_RRWheelBody && m_LFRevJoint && m_RFRevJoint && m_LRPrisJoint && m_RRPrisJoint)
			{
				removeOrthogonalVelocity(m_LFWheelBody);
				removeOrthogonalVelocity(m_RFWheelBody);
				removeOrthogonalVelocity(m_LRWheelBody);
				removeOrthogonalVelocity(m_RRWheelBody);
				
				//Driving
				var leftDir:b2Vec2 = m_LFWheelBody.GetTransform().R.col2.Copy();
				leftDir.Multiply(m_nEngineSpeed);
				var rightDir:b2Vec2 = m_RFWheelBody.GetTransform().R.col2.Copy();
				rightDir.Multiply(m_nEngineSpeed);
				m_LFWheelBody.ApplyForce(leftDir, m_LFWheelBody.GetPosition());
				m_RFWheelBody.ApplyForce(rightDir, m_RFWheelBody.GetPosition());
				
				//Steering
				var nMSpeed:Number = 0;
				nMSpeed = m_nSteeringAngle - m_LFRevJoint.GetJointAngle();
				m_LFRevJoint.SetMotorSpeed(nMSpeed * nSTEER_SPEED);
				nMSpeed = m_nSteeringAngle - m_RFRevJoint.GetJointAngle();
				m_RFRevJoint.SetMotorSpeed(nMSpeed * nSTEER_SPEED);
			}
		}
		
		public function destroy():void
		{
			m_stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			m_stage.removeEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
			
			var iJointVecLen:int = m_vJoints.length;
			if (m_vJoints)
			{
				for each(var joint:b2Joint in m_vJoints)
				{
					if (m_phyMgr) m_phyMgr.destroyJoint(joint);
					if (joint) joint = null;
				}
				m_vJoints.splice(0, iJointVecLen);
				m_vJoints = null;
			}
			
			var iBodiesVecLen:int = m_vBodies.length;
			if (m_vBodies)
			{
				for each(var body:b2Body in m_vBodies)
				{
					if (m_phyMgr) m_phyMgr.destroyBody(body);
					if (body) body = null;
				}
				m_vBodies.splice(0, iBodiesVecLen);
				m_vBodies = null;
			}
			
			if (m_position) m_position = null;
			
			if (m_stage && sprite)
			{
				if (m_stage.contains(sprite))
				{
					m_stage.removeChild(sprite);
				}
				sprite = null;
			}
			
			if (body)
			{
				m_phyMgr.destroyBody(body);
				body = null;
			}
			
			if (m_phyMgr)
			{
				m_phyMgr.destroy();
				m_phyMgr = null;
			}
			
			if (m_stage)
			{
				m_stage = null;
			}
		}
		
		/* private functions */
		private function createCar():void
		{
			var boxShapeChasis:b2Shape = m_phyMgr.createAABBShape(25, 40);
			var fixtureDefChasis:b2FixtureDef = m_phyMgr.createFixtureDef(boxShapeChasis, 1, 0, 0, false);
			m_b2Body = m_phyMgr.createBody(m_position.x, m_position.y, b2Body.b2_dynamicBody, fixtureDefChasis);
			
			var boxShapeWheel:b2Shape = m_phyMgr.createAABBShape(4, 10);
			var fixtureDefWheel:b2FixtureDef = m_phyMgr.createFixtureDef(boxShapeWheel, 1, 0, 0, true);
			m_LFWheelBody = m_phyMgr.createBody(m_position.x - nWHEEL_X, m_position.y - nWHEEL_Y, b2Body.b2_dynamicBody, fixtureDefWheel);
			m_RFWheelBody = m_phyMgr.createBody(m_position.x + nWHEEL_X, m_position.y - nWHEEL_Y, b2Body.b2_dynamicBody, fixtureDefWheel);
			m_LRWheelBody = m_phyMgr.createBody(m_position.x - nWHEEL_X, m_position.y + nWHEEL_Y, b2Body.b2_dynamicBody, fixtureDefWheel);
			m_RRWheelBody = m_phyMgr.createBody(m_position.x + nWHEEL_X, m_position.y + nWHEEL_Y, b2Body.b2_dynamicBody, fixtureDefWheel);
			
			m_LFRevJoint = m_phyMgr.createRevoluteJoint(false, m_b2Body, m_LFWheelBody, m_LFWheelBody.GetWorldCenter(), false, 0, 0, true, nTORQUE, 0);
			m_RFRevJoint = m_phyMgr.createRevoluteJoint(false, m_b2Body, m_RFWheelBody, m_RFWheelBody.GetWorldCenter(), false, 0, 0, true, nTORQUE, 0);
			
			m_LRPrisJoint = m_phyMgr.createPrismaticJoint(false, m_b2Body, m_LRWheelBody, m_LRWheelBody.GetWorldCenter(), new b2Vec2(1, 0), true, 0, 0);
			m_RRPrisJoint = m_phyMgr.createPrismaticJoint(false, m_b2Body, m_RRWheelBody, m_RRWheelBody.GetWorldCenter(), new b2Vec2(1, 0), true, 0, 0);
			
			m_vBodies.push(m_LFWheelBody);
			m_vBodies.push(m_RFWheelBody);
			m_vBodies.push(m_LRWheelBody);
			m_vBodies.push(m_RRWheelBody);
			
			m_vJoints.push(m_LFRevJoint);
			m_vJoints.push(m_RFRevJoint);
			m_vJoints.push(m_LRPrisJoint);
			m_vJoints.push(m_RRPrisJoint);
		}
		
		/**
		 * Applies friction in a direction orthagonal to the body's axis.
		 * @param	targetBody
		 */
		private function removeOrthogonalVelocity(targetBody:b2Body):void
		{
			if (targetBody)
			{
				var localPoint:b2Vec2 = new b2Vec2(0, 0);
				var velocity:b2Vec2 = targetBody.GetLinearVelocityFromLocalPoint(localPoint);
				
				var sidewaysAxis:b2Vec2 = targetBody.GetTransform().R.col2.Copy();
				sidewaysAxis.Multiply(b2Math.Dot(velocity, sidewaysAxis));
				targetBody.SetLinearVelocity(sidewaysAxis);
			}
		}
		
		/**
		 * Checks which keys are pressed and move the car.
		 */
		private function keyDownHandler(ke:KeyboardEvent):void
		{
			if (ke.keyCode == Keys.UP)
			{
				m_nEngineSpeed = -nHORSEPOWER;
			}
			if (ke.keyCode == Keys.DOWN)
			{
				m_nEngineSpeed = nHORSEPOWER;
			}
			if (ke.keyCode == Keys.LEFT)
			{
				m_nSteeringAngle = -nMAX_STEER_ANGLE;
			}
			if (ke.keyCode == Keys.RIGHT)
			{
				m_nSteeringAngle = nMAX_STEER_ANGLE;
			}
		}
		
		private function keyUpHandler(ke:KeyboardEvent):void
		{
			if (ke.keyCode == Keys.UP || ke.keyCode == Keys.DOWN)
			{
				m_nEngineSpeed = 0;
			}
			if (ke.keyCode == Keys.LEFT || ke.keyCode == Keys.RIGHT)
			{
				m_nSteeringAngle = 0;
			}
		}
		
	}

}
